//=============================================================================
/**
 *
 *	@file		drop_ai.c
 *	@brief		ポケスリング AI
 *	@author		hosaka genya
 *	@data		2009.01.07
 *
 */
//=============================================================================
#include "common.h"
#include "strbuf_family.h"
#include "system/clact_tool.h"
#include "system/gra_tool.h"
#include "system/msgdata_util.h"
#include "system/wordset.h"
#include "system/fontproc.h"

#include "poketool/pokeicon.h"

#include "msgdata/msg.naix"
#include "msgdata/msg_pokethlon.h"

#include "../../pkthln_comm_pack.h"
#include "../../pkthln_obj_common.h"
#include "../../pokethlon.h"
#include "../evt_cats.h"

#include "pkthln_drop.h"
#include "drop_define.h"
#include "drop_ai.h"

typedef struct DROP_AI_tag * DROP_AI_PTR;

//=============================================================================
/**
 *								定数定義
 */
//=============================================================================
enum
{
	AI_MAX = ENTRY_MEMBER_NUM_MAX-1,
};

//--------------------------------------------------------------
///
//==============================================================
typedef struct DROP_AI_tag {
	// [IN]
	PKTHLN_PTR work;
	// [PRIVATE]
	s8 ActTimer;
	s8 ChangeCount;
	s8 CheckWait;
	s8 LockID;
	u32 NetID : 2;
	u32 PokeID : 2;
	u32 bChangeMove : 1;	///< 交代移動フラグ
	u32 bLockOn : 1;
	u32 DummyBit : 26;
} DROP_AI;

//--------------------------------------------------------------
///
//==============================================================
typedef struct DROP_AI_CNT_tag {
	// [IN]
	HEAPID HeapID;
	PKTHLN_PTR work;
	// [PRIVATE]
	DROP_AI wkAI[ AI_MAX ];
	u8 AI_Count; ///< AIの数
	u8 DummyByte[3];
} DROP_AI_CNT;

//=============================================================================
/**
 *								構造体定義
 */
//=============================================================================

//=============================================================================
/**
 *							プロトタイプ宣言
 */
//=============================================================================
// prototype
static void DropAI_Setup( DROP_AI_PTR ptr, u8 NetID, PKTHLN_PTR work );
static void DropAI_Proc( DROP_AI_PTR ptr );


//=============================================================================
/**
 *								外部公開関数
 */
//=============================================================================

//-----------------------------------------------------------------------------
/**
 *	@brief	AIコントローラー 生成
 *
 *	@param	PKTHLN_PTR work
 *	@param	HeapID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
DROP_AI_CNT_PTR DropAICnt_Create( PKTHLN_PTR work, HEAPID HeapID )
{
	int i;
	DROP_AI_CNT_PTR cnt;
	u8 PlayerNum = PTFrame_GetPlayerNum(work);

	cnt = sys_AllocMemory( HeapID, sizeof(DROP_AI_CNT) );
	MI_CpuClear8( cnt, sizeof(DROP_AI_CNT) );

	cnt->HeapID = HeapID;
	cnt->work = work;
	cnt->AI_Count = ENTRY_MEMBER_NUM_MAX - PlayerNum;

	for( i=0; i<cnt->AI_Count; i++ )
	{
		DropAI_Setup( &cnt->wkAI[i], PlayerNum + i, work );
	}

	return cnt;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AIコントローラー 削除
 *
 *	@param	DROP_AI_CNT_PTR cnt
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void DropAICnt_Delete( DROP_AI_CNT_PTR cnt )
{
	sys_FreeMemoryEz( cnt );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AIコントローラー 主処理
 *
 *	@param	DROP_AI_CNT_PTR cnt
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void DropAICnt_Proc( DROP_AI_CNT_PTR cnt )
{
	int i;

	for( i=0; i<cnt->AI_Count; i++ )
	{
		DropAI_Proc( &cnt->wkAI[i] );
	}
}

//=============================================================================
/**
 *								static関数
 */
//=============================================================================
//-----------------------------------------------------------------------------
/**
 *	@brief	AI初期化
 *
 *	@param	DROP_AI_PTR ptr
 *	@param	NetID
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void DropAI_Setup( DROP_AI_PTR ptr, u8 NetID, PKTHLN_PTR work )
{
	MI_CpuClear8( ptr, sizeof(DROP_AI) );

	ptr->work = work;
	ptr->NetID = NetID;
	ptr->PokeID = 0;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	2点の距離判定
 *
 *	@param	VecFx32* vecA			座標A
 *	@param	VecFx32* vecB			座標B
 *	@param	fx_range				半径
 *
 *	@retval	TRUE:接触している
 */
//-----------------------------------------------------------------------------
static BOOL Check_HitVec( VecFx32* vecA, VecFx32* VecB, fx32 fx_range )
{
	fx32 len;
	VecFx32 dir_vec;

	VEC_Subtract( VecB, vecA, &dir_vec );
	len = VEC_Mag(&dir_vec);

	return ( len <= fx_range );
}

//--------------------------------------------------------------
///	タックル優先度(高いほうを優先)
//==============================================================
enum
{
	TKL_PRI_NONE = 0, ///<タックル対象外
	TKL_PRI_S_OUT,	///< 近距離視野外
	TKL_PRI_L_IN,	///< 遠距離視野内
	TKL_PRI_S_IN,	///< 近距離視野内
} TACKLE_PRIORITY;

//--------------------------------------------------------------
///
//==============================================================
typedef struct {
	u8 NetID;
	u8 Pri;
	u8 DummyByte[2];
} TACKLE_DATA;

//-----------------------------------------------------------------------------
/**
 *	@brief	視野チェック
 *
 *	@param	DROP_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL Check_Vision( VecFx32* pMyVec, VecFx32* pTarVec, u8 MyDir )
{
	VecFx32 Intersect;

	VEC_Subtract( pTarVec, pMyVec, &Intersect );
	VEC_Normalize( &Intersect, &Intersect );

	if( MyDir == Drop_CalcPokeDir( &Intersect ) )
	{
		return TRUE;
	}

	return FALSE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	リング状況から行動チェック
 *
 *	@param	DROP_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Check_Ring( DROP_AI_PTR ptr )
{
	VecFx32* pMyVec;
	POKE_DIR MyDir;
	int i;
	u8 cnt_l = 0;
	u8 cnt_s = 0;
	u8 tkl_pri[ENTRY_MEMBER_NUM_MAX] = {0};

	pMyVec = Drop_GetPokePos( ptr->work, ptr->NetID );
	MyDir = Drop_GetPokeDir( ptr->work, ptr->NetID );

	// タックル判定用のデータ集計
	for (i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		VecFx32* pTarVec;

		if( i==ptr->NetID ){ continue; } // 自分は除外

		pTarVec = Drop_GetPokePos( ptr->work, i );

		if( Check_HitVec( pMyVec, pTarVec, AI_TACKLE_RANGE_S_FX ) )
		{
			if( Check_Vision( pMyVec, pTarVec, MyDir ) )
			{
				tkl_pri[i] = TKL_PRI_S_IN;
			}
			else
			{
				tkl_pri[i] = TKL_PRI_S_OUT;
			}
			cnt_s++;
		}
		else if( Check_HitVec( pMyVec, pTarVec, AI_TACKLE_RANGE_L_FX ) )
		{
			if( Check_Vision( pMyVec, pTarVec, MyDir ) )
			{
				tkl_pri[i] = TKL_PRI_L_IN;
			}
			cnt_l++;
		}
	}

	{
		int rand;
		u8 Par_Tackle = 0;
		u8 Par_Jump = 0;
		TACKLE_DATA tackle_target = {0};

		// タックル対象を抽選
		for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
		{
			if( i==ptr->NetID ){ continue; } // 自分は除外

			// プライオリティが高い奴を取得
			if( tackle_target.Pri < tkl_pri[i] )
			{
				tackle_target.NetID = i;
				tackle_target.Pri = tkl_pri[i];
			}
		}

		rand = gf_mtRand() % 100;

		Par_Tackle += g_AI_TacklePar[ tackle_target.Pri ];

		// タックル判定
		if( rand < Par_Tackle )
		{
			VecFx32* pTarVec;
			POS_DATA TarPos;

			pTarVec = Drop_GetPokePos( ptr->work, tackle_target.NetID );

			TarPos.X = pTarVec->x >> FX32_SHIFT;
			TarPos.Y = pTarVec->y >> FX32_SHIFT;
			Drop_StartTackle( ptr->work, ptr->NetID, &TarPos );
			ptr->ActTimer = AI_TIMER_TACKLE;
			return; ///< タックルが発動したら処理を打ち切る
		}

		// 抽選しなおす
		rand = gf_mtRand() % 100;

		// 遠距離にポケモンがいたらジャンプ確率を加算
		if( cnt_l > 0 )
		{
			Par_Jump += AI_JUMP_PAR_L;
		}

		// 近距離のポケモン数からジャンプ確率を加算
		if( cnt_s > 0 )
		{
			Par_Jump += g_AI_JumpParS[cnt_s-1];
		}

		// ジャンプ判定
		if( rand < Par_Jump )
		{
			// ジャンプ処理
			Drop_StartJumpPress( ptr->work, ptr->NetID );
			ptr->ActTimer = AI_TIMER_JUMP;
			return; ///< ジャンプが発生したら処理を打ち切る
		}
	}

	// 移動処理
	// ポケモン未発見、もしくはタックル・ジャンプ判定抜け
	{
		static const move_par[] = {
			AI_MOVE_PAR_TRACKING,
			AI_MOVE_PAR_TRACKING + AI_MOVE_PAR_WAIT,
			AI_MOVE_PAR_TRACKING + AI_MOVE_PAR_WAIT + AI_MOVE_PAR_RAND,
		};

		int rand = gf_mtRand() % 100;

		// 追尾
		if( rand < move_par[0] )
		{
			int tr_rand = gf_mtRand() % 100;

			if( ptr->bLockOn == FALSE )
			{
				s8 LowID_1 = -1;
				s8 LowID_2 = -1;
				u16 LowHP = 999;

				// 一番、2番にHPが低い奴を探す
				for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
				{
					u8 GetHP = Drop_GetPokeStamina( ptr->work, i );
					POKE_STATE State = Drop_GetPokeState( ptr->work, i );

					if( i == ptr->NetID ){ continue; } // 自分は除外

					// 落下、復活、交代中の奴は省く
					if( State != POKE_ST_FALL && State != POKE_ST_REVIVE && ( Drop_GetChangeFlag( ptr->work, ptr->LockID ) == FALSE ) )
					{
						if( LowHP > GetHP )
						{
							LowID_2 = LowID_1;
							LowID_1 = i;
							LowHP = GetHP;
						}
					}
				}

				if( tr_rand < AI_TRACKLING_PAR_LOW1 )
				{
					ptr->LockID = LowID_1;
				}
				else if ( tr_rand < AI_TRACKLING_PAR_LOW1 + AI_TRACKLING_PAR_LOW2 )
				{
					ptr->LockID = LowID_2;
				}
				else
				{
					GF_ASSERT(0);
				}

				if( ptr->LockID != -1 )
				{
					ptr->bLockOn = 1;
					ptr->ActTimer = AI_TIMER_TRACKING;
				}
			}
		}
		// 静止
		else if( rand < move_par[1] )
		{
			ptr->ActTimer = AI_TIMER_WAIT;
		}
		// ランダム移動
		else if( rand < move_par[2] )
		{
			POS_DATA Pos;
			Pos.X = gf_mtRand() % 145 + 56;
			Pos.Y = gf_mtRand() % 80 + 72;
			Drop_SetMovePos( ptr->work, ptr->NetID, &Pos );
			ptr->ActTimer = AI_TIMER_RAND;
		}
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	交代移動処理
 *
 *	@param	DROP_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void DropAI_Change( DROP_AI_PTR ptr )
{
	const POS_DATA* pMovePos = &g_AI_ChangeTarget[ptr->NetID];
	int poke_x, poke_y;
	VecFx32 ShadowVec = {0};
	VecFx32 TarVec = {0};

	Drop_GetPokePosShadow( ptr->work, ptr->NetID, &ShadowVec );

	TarVec.x = g_AI_ChangeTarget[ptr->NetID].X * FX32_ONE;
	TarVec.y = g_AI_ChangeTarget[ptr->NetID].Y * FX32_ONE;

	// 目的地を設定
	Drop_SetMovePos( ptr->work, ptr->NetID, pMovePos );

	// 交代判定
	if( Check_HitVec( &ShadowVec, &TarVec, AI_CHANGE_RANGE_FX ) )
	{
		Drop_StartChange( ptr->work, ptr->NetID );
		ptr->bChangeMove = 0;
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	交代開始判定
 *
 *	@param	DROP_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL Check_Change( DROP_AI_PTR ptr )
{
	int rand = gf_mtRand() % 100;
	int ChgPar = 0;	///< 交代確率
	u8 StaPar = Drop_GetPokeStaminaParsent( ptr->work, ptr->NetID );

	if( StaPar < 10 )
	{
		ChgPar = 100;
	}
	else if( StaPar < 20 )
	{
		ChgPar = 70;
	}
	else if( StaPar < 30 )
	{
		ChgPar = 50;
	}
	else if( StaPar < 40 )
	{
		ChgPar = 30;
	}
	else if( StaPar < 50 )
	{
		ChgPar = 10;
	}

	return ( rand < ChgPar );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AI主処理
 *
 *	@param	DROP_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void DropAI_Proc( DROP_AI_PTR ptr )
{
	// 自分が交代中はAI処理を入れない
	if( Drop_GetChangeFlag( ptr->work, ptr->NetID ) )
	{
		return;
	}

	// 追尾処理
	if( ptr->bLockOn && (!ptr->bChangeMove) )
	{
		VecFx32* pVec;
		POS_DATA Pos;
		POKE_STATE State = Drop_GetPokeState( ptr->work, ptr->NetID );
		POKE_STATE TarState = Drop_GetPokeState( ptr->work, ptr->LockID );

		// ターゲットを見失う処理 ( 相手が落下、交代もしくは自分が衝突された )
		if( TarState == POKE_ST_FALL || Drop_GetChangeFlag( ptr->work, ptr->LockID ) || Drop_GetPushFlag( ptr->work, ptr->NetID ) )
		{
			pVec = Drop_GetPokePos( ptr->work, ptr->NetID );
			Pos.X = pVec->x >> FX32_SHIFT;
			Pos.Y = pVec->y >> FX32_SHIFT;
			Drop_SetMovePos( ptr->work, ptr->NetID, &Pos );
			ptr->bLockOn = 0;
		}
		else
		{
			// 追尾処理
			pVec = Drop_GetPokePos( ptr->work, ptr->LockID );
			Pos.X = pVec->x >> FX32_SHIFT;
			Pos.Y = pVec->y >> FX32_SHIFT;
			Drop_SetMovePos( ptr->work, ptr->NetID, &Pos );
		}
	}

	// 交代移動処理
	if( ptr->bChangeMove )
	{
		if( Drop_CheckPokeEnable( ptr->work, ptr->NetID ) )
		{
			POKE_STATE State = Drop_GetPokeState( ptr->work, ptr->NetID );

			DropAI_Change( ptr );

			// 落下したら交代フラグを折る
			if( State == POKE_ST_FALL )
			{
				ptr->bChangeMove = 0;
			}
		}
	}
	// AI駆動
	else
	{
		if( --ptr->ActTimer <= 0 )
		{
			// 回りこみ防止
			if( ptr->ActTimer < 0 ){ ptr->ActTimer = 0; }

			if( --ptr->CheckWait <= 0 )
			{
				ptr->CheckWait = AI_CALC_SPAN;

				if( Drop_CheckPokeEnable( ptr->work, ptr->NetID ) )
				{
					Check_Ring( ptr );

					if( --ptr->ChangeCount <= 0 )
					{
						// 交代判定
						if( Check_Change( ptr ) )
						{
							ptr->bChangeMove = 1;
							HOSAKA_PRINT("[%d] AI Change Start!\n",ptr->NetID);
						}
						// 再度判定するまでのカウンタ
						ptr->ChangeCount = AI_CHANGE_COUNTER;
					}
				}
			}
		}
	}
}
